﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using avThreading;

namespace GZSolver
{
    class ParallelGausZeidelLinearSystemCalculator : LinearSystemCalculator
    {
        public override LinearSystemSolvingResults SolveLinearSystem(Matrix coeffs, double[] b, double epsilon)
        {
            LinearSystemSolvingResults result = new LinearSystemSolvingResults();
            
            double[] globalResultVector = new double[coeffs.RowCount];
            
            int rectangleMatrixSize = coeffs.RowCount;
            int numOfThreads = 2;
            int nLocal = rectangleMatrixSize / numOfThreads;
            int globalCounter = 0;
            double globalDelta = 0.0;
            // Stopwatch for measurement algorithm performance
            Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();

            // Locking variable.
            object threadLocker = new object();

            using (new Execute(0, rectangleMatrixSize, numOfThreads, delegate(int start, int end, int meIndex)
                {
                    do
                    {
                        double[] tempResultVector = new double[coeffs.RowCount];
                        double localDelta = 0.0;
                        globalCounter++;

                        for (int i = 0; i < rectangleMatrixSize; i++)
                        {
                            double s_k = 0.0;
                            for (int j = 0; j < nLocal; j++)
                            {
                                if (j + meIndex * nLocal != i)
                                {
                                    s_k += coeffs[i][j] * tempResultVector[j];
                                }
                                int root = i / nLocal;
                                int iLocal = i % nLocal;

                                // TODO: Wait until all threads will calculater s_k for iLocal index and sum them.

                                globalResultVector[iLocal] += s_k;

                                if (meIndex == root)
                                {
                                    double x_new = (b[iLocal] - globalResultVector[iLocal]) / coeffs[i][iLocal];
                                    localDelta = Math.Max(localDelta, Math.Abs(globalResultVector[iLocal] - x_new));
                                    globalResultVector[iLocal] = x_new;
                                }
                            }
                        }
                        lock (threadLocker)
                        {
                            globalDelta = Math.Max(globalDelta, localDelta);
                            return;
                        }
                    }
                    while (globalDelta > epsilon);
                }
            )) ;

            stopWatch.Stop();

            result.ResultVector = globalResultVector;
            result.SolvingTime = stopWatch.ElapsedMilliseconds;
            result.ElapsedTicks = stopWatch.ElapsedTicks;

            return result;
        }

    }
}
